package net.lulihu.common_util.junitTest;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import net.lulihu.ObjectKit.BeanKit;
import net.lulihu.ObjectKit.ReflectKit;
import net.lulihu.ObjectKit.StrKit;
import org.junit.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.context.WebApplicationContext;

import java.lang.reflect.Field;
import java.util.List;

@Slf4j
public abstract class SpringbootBaseTests {

    @Autowired
    private WebApplicationContext context;

    private MockMvc mockMvc;

    @Before
    public void setupMockMvc() {
        mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
    }

    /**
     * 设置请求头
     */
    public void setHeaders(HttpHeaders httpHeaders) {
    }

    /*---------------------------POST----------------------------*/
    public MvcResult sendPostRequest(String url) throws Exception {
        return sendRequest(url, HttpMethod.POST);
    }

    public MvcResult sendPostRequestContent(String url, Object param) throws Exception {
        return sendRequestContent(url, HttpMethod.POST, JSON.toJSONString(param));
    }

    public MvcResult sendPostRequestParam(String url, Object param) throws Exception {
        LinkedMultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap<>();

        List<Field> fields = ReflectKit.getAllFieldsList(param.getClass());
        for (Field field : fields) {
            if (!BeanKit.isPrimitive(field.getType()))
                throw new Exception(
                        StrKit.format("对象属性【{}】为非基本数据类型，可能导致请求异常，复杂数据请求请使用（sendPostRequestContent）", field.getName()));

            Object value = ReflectKit.getFieldValue(param, field);
            if (value == null) continue;
            multiValueMap.set(field.getName(), String.valueOf(value));
        }
        return sendRequestParam(url, HttpMethod.POST, multiValueMap);
    }

    public MvcResult sendPostRequestParam(String url, MultiValueMap<String, String> param) throws Exception {
        return sendRequestParam(url, HttpMethod.POST, param);
    }
    /*---------------------------GET----------------------------*/

    public MvcResult sendGetRequest(String url) throws Exception {
        return sendRequest(url, HttpMethod.GET);
    }

    public MvcResult sendGetRequestParam(String url, MultiValueMap<String, String> param) throws Exception {
        return sendRequestParam(url, HttpMethod.GET, param);
    }

    /*---------------------------Request----------------------------*/

    private MvcResult sendRequest(String url, HttpMethod requestMethod) throws Exception {
        return sendRequestParam(url, requestMethod, null);
    }

    private MvcResult sendRequestParam(String url, HttpMethod requestMethod, MultiValueMap<String, String> param) throws Exception {
        // 设置token
        HttpHeaders httpHeaders = new HttpHeaders();
        setHeaders(httpHeaders);
        return sendRequest(url, requestMethod, param, httpHeaders);
    }

    private MvcResult sendRequestContent(String url, HttpMethod requestMethod, String content) throws Exception {
        // 设置token
        HttpHeaders httpHeaders = new HttpHeaders();
        setHeaders(httpHeaders);
        return sendRequest(url, requestMethod, null, content, httpHeaders);
    }

    /**
     * 发送接口测试请求
     *
     * @param url           路径
     * @param requestMethod 请求方式
     * @param param         参数
     * @param httpHeaders   请求头
     * @throws Exception Exception
     */
    private MvcResult sendRequest(String url, HttpMethod requestMethod,
                                  MultiValueMap<String, String> param,
                                  HttpHeaders httpHeaders) throws Exception {
        return sendRequest(url, requestMethod, param, null, httpHeaders);
    }

    /**
     * 发送接口测试请求
     *
     * @param url           路径
     * @param requestMethod 请求方式
     * @param param         参数
     * @param content       请求正文
     * @param httpHeaders   请求头
     * @throws Exception Exception
     */
    private MvcResult sendRequest(final String url,
                                  HttpMethod requestMethod,
                                  MultiValueMap<String, String> param,
                                  final String content,
                                  HttpHeaders httpHeaders) throws Exception {
        MockHttpServletRequestBuilder mockBuilder;
        switch (requestMethod) {
            case GET:
                mockBuilder = MockMvcRequestBuilders.get(url);
                break;
            case HEAD:
                mockBuilder = MockMvcRequestBuilders.head(url);
                break;
            case POST:
                mockBuilder = MockMvcRequestBuilders.post(url);
                break;
            case PUT:
                mockBuilder = MockMvcRequestBuilders.put(url);
                break;
            case PATCH:
                mockBuilder = MockMvcRequestBuilders.patch(url);
                break;
            case DELETE:
                mockBuilder = MockMvcRequestBuilders.delete(url);
                break;
            case OPTIONS:
                mockBuilder = MockMvcRequestBuilders.options(url);
                break;
            default:
                throw new IllegalArgumentException("不支持的请求类型！");
        }
        if (param != null) {
            mockBuilder.params(param);
        }
        if (content != null) {
            mockBuilder.content(content);
        }
        if (httpHeaders != null) {
            mockBuilder.headers(httpHeaders);
        }
        mockBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
        return mockMvc.perform(mockBuilder)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_UTF8))
                .andDo(MockMvcResultHandlers.print())
                .andReturn();
    }
}
